package com.cmcc.sdtp.sdtp.util;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.SignatureException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.cmcc.sdtp.sdtp.message.LinkAuthReqMsg;
import com.cmcc.sdtp.sdtp.message.LinkAuthRespMsg;
import com.cmcc.sdtp.sdtp.message.LinkCheckRespMsg;
import com.cmcc.sdtp.sdtp.message.LinkDataCheckReqMsg;
import com.cmcc.sdtp.sdtp.message.LinkDataCheckRespMsg;
import com.cmcc.sdtp.sdtp.message.LinkRelRespMsg;
import com.cmcc.sdtp.sdtp.message.MessageType;
import com.cmcc.sdtp.sdtp.message.SDTPMessage;
import com.cmcc.sdtp.sdtp.message.VerNegoReqMsg;
import com.cmcc.sdtp.sdtp.message.VerNegoRespMsg;
import com.cmcc.sdtp.sdtp.message.XDRDataNotifyRespMsg;
import com.cmcc.sdtp.sdtp.message.XDRRawDataSendReqMsg;
import com.cmcc.sdtp.sdtp.message.XDRRawDataSendRespMsg;
import com.cmcc.sdtp.util.SdtpPropertiesUtil;
import com.cmcc.xdr.message.IXDRData;
import com.google.common.hash.HashCode;

public class MessageUtil {

	private static final int majorVersion = Integer.valueOf(SdtpPropertiesUtil.getProperty("Version"));
	private static final int minorVersion = Integer.valueOf(SdtpPropertiesUtil.getProperty("SubVersion"));

	private static Logger logger = LogManager.getLogger(MessageUtil.class);

	private static SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
	/**
	 * byte,byte数组默认填充值(0xFF)
	 */
	public static final byte DEFAULT_BYTE = (byte) -1;

	/**
	 * 长度不够时填充字节,为空格(ascii 32)
	 */
	public static final byte WHITE_BYTE = (byte) 32;

	/**
	 * 将int转换为byte数组
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] toByteArray(double data, int length) {
		byte[] bytes = new byte[length];
		ByteBuffer.wrap(bytes).putDouble(length - 8, data);
		return bytes;
	}

	/**
	 * 将无符号long转换为byte数组,向下兼容int,short,byte
	 * 
	 * @param data
	 * @param length
	 * @return
	 */
	// XXX 注意!有符号的数字转换可能会出错
	public static byte[] toByteArray(long data, int length) {

		byte[] bufParam = new byte[length];
		bufParam[length - 1] = (byte) data;
		for (int i = 1; i < bufParam.length; i++) {
			bufParam[length - 1 - i] = (byte) (data >>> (8 * i));
		}
		return bufParam;
	}

	// TODO 等规则统一化确定是左补还是右补0
	/**
	 * 字符串转换为byte数组
	 * @param data
	 * @param length
	 * @return
	 */
	public static byte[] toByteArray(String data, int length) {
		byte[] bytes = new byte[length];
		// 数组总长-数据占字节数 = 需要左补偏移量
		byte[] strBytes = data.getBytes();
		ByteBuffer buffer = ByteBuffer.wrap(bytes);

		int offset = length - strBytes.length;
		if (offset < 0) {
			throw new IllegalArgumentException("数据超出规定长度,数据内容:" + data);
		} else if (offset > 0) {// 数据不足以填充byte[]需要补空白字符
			buffer.put(fillByteArray(offset, WHITE_BYTE));
		}
		buffer.put(strBytes);
		return bytes;
	}

	public static byte[] toByteArrayFromHex(String data, int length) {
		byte[] result = new byte[length];
		byte[] strBytes = HashCode.fromString(data.toLowerCase()).asBytes();
		ByteBuffer buffer = ByteBuffer.wrap(result);
		int offset = length - strBytes.length;
		if (offset < 0) {
			throw new IllegalArgumentException("数据超出规定长度,数据内容:" + data);
		} else if (offset > 0) {// 数据不足以填充byte[]需要补空白字符
			buffer.put(fillByteArray(offset, WHITE_BYTE));
		}
		buffer.put(strBytes);
		return buffer.array();
	}

	public static byte[] toByteArrayFromHex(String data) {
		return toByteArrayFromHex(data, data.length() / 2);
	}

	/**
	 * 根据ip字符串返回byte数组,如果长度不够会在头部之前插入0xFF
	 * 
	 * @param data
	 * @param length
	 * @return
	 */
	public static byte[] toByteArrayFromIP(String data, int length) {
		ByteBuffer buffer = ByteBuffer.allocate(length);
		byte[] temp = null;
		try {
			InetAddress address = InetAddress.getByName(data);
			temp = address.getAddress();
		} catch (UnknownHostException e) {
			throw new RuntimeException(e.getMessage());
		}
		int offset = length - temp.length;
		// 长度不足部分在头部之前插入0xFF
		if (offset > 0) {
			buffer.put(fillByteArray(offset, DEFAULT_BYTE));
		}
		buffer.put(temp);

		return buffer.array();

	}

	/**
	 * 字节数组转换为double
	 * 
	 * @param data
	 * @return
	 */
	public static double toDouble(byte[] data) {
		return ByteBuffer.wrap(data).getDouble();
	}

	/**
	 * 将无符号数字的byte数据转换为BigInteger
	 * 
	 * @param arr
	 * @return
	 */
	// XXX 注意!有符号的数字转换可能会出错
	public static BigInteger toBigInteger(byte[] arr) {
		// 在原数组前插入一字节作为符号位
		byte[] result = new byte[arr.length + 1];
		for (int i = arr.length; i > 0; i--) {
			result[i] = arr[i - 1];
		}
		return new BigInteger(result);
	}

	/**
	 * @param arr
	 * @return
	 */
	public static String toHexString(byte[] arr) {
		return HashCode.fromBytes(arr).toString();
	}

	public static String formatDateTime(Object date) {
		return format.format(date);
	}

	public static String toIpAddress(byte[] bytes) {
		if (bytes[0] == -1 && bytes.length > 4) {
			bytes = Arrays.copyOfRange(bytes, bytes.length - 4, bytes.length);
		}
		try {
			return InetAddress.getByAddress(bytes).getHostAddress();
		} catch (UnknownHostException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 返回指定长度的数组,数组填充内容为0xFF
	 * 
	 * @param length
	 * @return
	 */
	public static byte[] fillByteArray(int length) {
		return fillByteArray(length, DEFAULT_BYTE);
	}

	/**
	 * 返回指定长度的数组,数组填充内容为fillByte
	 * 
	 * @param length
	 * @param fillByte
	 * @return
	 */
	public static byte[] fillByteArray(int length, byte fillByte) {
		byte[] temp = new byte[length];
		Arrays.fill(temp, fillByte);
		return temp;
	}

	/**
	 * @comment 处理版本协商请求
	 * 
	 * @param msgBody
	 * @return
	 */
	public static SDTPMessage dealVerNego(SDTPMessage reqMsg) {
		logger.debug("===>处理版本协商请求 dealVerNego<===");
		VerNegoReqMsg reqBody = (VerNegoReqMsg) reqMsg.getMsgBody();

		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.verNego_Resp);
		respMsg.setSequenceId(reqMsg.getSequenceId());

		VerNegoRespMsg respbody = new VerNegoRespMsg();
		respbody.setMessage(respMsg);
		int result = 1;
		result = Integer.compare(reqBody.getVersion(), majorVersion); // 比较主版本号
		if (result == 0) {
			result = Integer.compare(reqBody.getSubVersion(), minorVersion);
			// 比较字版本号
		}

		if (result == 0) { // 版本协商通过
			result = 1;
			logger.info("===处理版本协商请求==比较:版本协商通过="+result);
		} else if (result > 0) { // 版本过高。
			result = 2;
			logger.error("===处理版本协商请求==比较:版本过高="+result);
		} else { // 版本过低
			logger.error("===处理版本协商请求==比较:版本过低="+result);
			result = 3;
		}
		respbody.setResult(result);
		respMsg.encodeMessage();
		logger.debug("===处理版本协商请求==结束=");
		return respMsg;
	}

	/**
	 * @comment 处理鉴权请求
	 * 
	 * @param reqMsg
	 * @return
	 */
	public static SDTPMessage dealLinkAuth(SDTPMessage reqMsg) {
		logger.debug("===>处理鉴权请求 dealLinkAuth<===");
		LinkAuthReqMsg reqBody = (LinkAuthReqMsg) reqMsg.getMsgBody();
		// 构造响应包消息头
		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.linkAuth_Resp);
		respMsg.setSequenceId(reqMsg.getSequenceId());

		LinkAuthRespMsg respBody = new LinkAuthRespMsg();
		respBody.setMessage(respMsg);
		int result = 1;

		String resultDigest = "";

		try {
			resultDigest = AuthUtil.linkAuth(reqBody);
		} catch (SignatureException e) { // LoginID不存在
			logger.error("===处理鉴权请求==LoginID不存在=",e);
			result = 2;
		} catch (DigestException e) { // SHA256加密结果出错。
			logger.error("===处理鉴权请求==SHA256加密结果出错。=",e);
			result = 3;
		}
		resultDigest = "";
		respBody.setResult(result);
		respBody.setDigest(resultDigest);
		respMsg.encodeMessage();
		logger.debug("===处理鉴权请求==结束=");
		return respMsg;
	}

	/**
	 * 处理链路数据发送校验请求
	 * 
	 * @param reqMsg
	 * @param recDataInfo
	 * @return
	 */
	public static SDTPMessage dealLinkDataCheck(SDTPMessage reqMsg, int recDataInfo) {
		logger.debug("===>处理链路数据发送校验请求 dealLinkDataCheck<===");

		LinkDataCheckReqMsg reqBody = (LinkDataCheckReqMsg) reqMsg.getMsgBody();

		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.linkDataCheck_Resp);
		respMsg.setSequenceId(reqMsg.getSequenceId());

		LinkDataCheckRespMsg respBody = new LinkDataCheckRespMsg();
		respBody.setMessage(respMsg);
		int sendDataInfo = reqBody.getSendDataInfo();
		int result = sendDataInfo - recDataInfo;
		if (result < 0) { // 接收方数据大于发送方数据
			logger.error("===处理链路数据发送校验请求 ==接收方数据大于发送方消息包数量,发送方sendDataInfo:"+sendDataInfo+",接受方recDataInfo:"+recDataInfo+"相差result="+result);
			result = 2;
		} else if (result > 0) { // 接收方数据小于发送方数据
			logger.error("===处理链路数据发送校验请求 <==接收方数据小于发送方消息包数量,发送方sendDataInfo:"+sendDataInfo+",接收方recDataInfo:"+recDataInfo+"相差result="+result);
			result = 1;
		}
		respBody.setResult(result);
		respBody.setSendFlag(reqBody.getSendFlag());
		respBody.setSendDataInfo(reqBody.getSendDataInfo());
		respBody.setRecDataInfo(recDataInfo);

		respMsg.encodeMessage();
		logger.debug("===处理链路数据发送校验请求 ==结束=result="+result+",sendDataInfo:"+sendDataInfo+",recDataInfo:"+recDataInfo);
		return respMsg;
	}

	/**
	 * 处理链路检测请求
	 * 
	 * @param reqMsg
	 * @return
	 */
	public static SDTPMessage dealLinkCheck(SDTPMessage reqMsg) {
		logger.debug("===>处理链路检测请求 dealLinkCheck<===");
		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.linkCheck_Resp);
		respMsg.setSequenceId(reqMsg.getSequenceId());

		LinkCheckRespMsg respBody = new LinkCheckRespMsg();
		respBody.setMessage(respMsg);

		respMsg.encodeMessage();
		logger.debug("===处理链路检测请求=结束==");
		return respMsg;
	}

	/**
	 * 处理链接释放请求
	 * 
	 * @param reqMsg
	 * @return
	 */
	public static SDTPMessage dealLinkRel(SDTPMessage reqMsg) {
		logger.debug("===>处理链接释放请求 dealLinkRel<===");
		int sequenceId = 0;
		if (Util.checkNotNull(reqMsg)) {
			sequenceId = reqMsg.getSequenceId();
		}
		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.linkRel_Resp);
		respMsg.setSequenceId(sequenceId);

		LinkRelRespMsg respBody = new LinkRelRespMsg();
		respBody.setMessage(respMsg);
		// 真是莫名其妙，服务器有没有释放成功在给客户端发消息时候怎么会知道。。人家全释放了怎么发消息。。
		respBody.setResult(1);

		respMsg.encodeMessage();
		logger.debug("===处理链接释放请求==结束=");
		return respMsg;
	}

	/**
	 * @Comment 处理XDR数据包
	 * 
	 * @param message
	 * @return
	 */
	public static SDTPMessage dealXDRDataNoify(SDTPMessage reqMsg) {
		// XDRDataNotifyReqMsg reqBody = (XDRDataNotifyReqMsg)
		// reqMsg.getMsgBody();
		int result = 1;
		// try {
		// saveXDRDataNoify(reqBody.getXdrDataList());
		// result = 1;
		// } catch (Exception e) {
		// logger.warn("入库过程中发生异常", e);
		// }
		logger.debug("===>处理处理XDR数据包 dealXDRDataNoify<===");
		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.notifyXDRData_Resp);
		respMsg.setSequenceId(reqMsg.getSequenceId());
		XDRDataNotifyRespMsg respBody = new XDRDataNotifyRespMsg();
		respBody.setMessage(respMsg);
		// 接收成功
		respBody.setResult(result);
		respMsg.encodeMessage();
		logger.debug("===处理处理XDR数据包=结束==");
		return respMsg;
	}

	private static void saveXDRDataNoify(List<IXDRData> xdrDataList) {
		for (IXDRData ixdrData : xdrDataList) {
			// TODO 在这里入库
		}
	}

	/**
	 * @Comment 处理XDR数据包
	 * 
	 * @param message
	 * @return
	 */
	public static SDTPMessage dealXDRDataSend(SDTPMessage reqMsg) {
		logger.debug("===>处理XDR数据包 dealXDRDataSend<===");
		XDRRawDataSendReqMsg reqBody = (XDRRawDataSendReqMsg) reqMsg.getMsgBody();
		int result = 0;
		try {
			saveXDRRawDataSend(reqBody.getXdrDataList());
			result = 1;
		} catch (Exception e) {
			logger.error("===>处理XDR数据包 dealXDRDataSend<===",e);
		}

		SDTPMessage respMsg = new SDTPMessage();
		respMsg.setMessageType(MessageType.XDRRawDataSend_Resp);
		respMsg.setSequenceId(reqMsg.getSequenceId());
		XDRRawDataSendRespMsg respBody = new XDRRawDataSendRespMsg();
		respBody.setMessage(respMsg);
		respBody.setResult(result);
		respMsg.encodeMessage();
		logger.debug("===处理XDR数据包 dealXDRDataSend==结束=");
		return respMsg;
	}

	private static void saveXDRRawDataSend(List<IXDRData> xdrDataList) {
		// TODO 在这里入库
		for (IXDRData ixdrData : xdrDataList) {
		}
	}

}